#version 320 es
#ifdef GL_ES
	precision highp float;
#endif
#include "ellipse.glsl"
#include "fadeOut.glsl"

out vec4 FragColor;

in vec3 ioNormal;
in vec3 ioViewVector;
in vec2 ioTexcoord;

uniform bool isTexImage;
uniform sampler2D textureImg;

// material parameters
struct Material {
	vec3 baseColor;
	float metallic;
	float roughness;
};
uniform Material material;

uniform bool isZhiche;

uniform bool enableLight;

uniform float pbrGlobalAmbientCompnt;

// lights
struct DirectionalLight {
	vec3 worldDirection;
	float intensity;
	vec3 color;
};
uniform DirectionalLight directional;

const float PI = 3.14159265359;
const float F0_Compnt = 0.3;// Base Reflectivity
const float AO_Factor = 0.8;// Ambient Occlusion

// ----------------------------------------------------------------------------
float DistributionGGX(vec3 N, vec3 H, float roughness) {
	float a = roughness * roughness;
	float a2 = a*a;
	float NdotH = max(dot(N, H), 0.0);
	float NdotH2 = NdotH*NdotH;

	float nom   = a2;
	float denom = (NdotH2 * (a2 - 1.0) + 1.0);
	denom = PI * denom * denom;

	return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySchlickGGX(float NdotV, float roughness) {
	float r = (roughness + 1.0);
	float k = (r*r) / 8.0;

	float nom   = NdotV;
	float denom = NdotV * (1.0 - k) + k;

	return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
	float NdotV = max(dot(N, V), 0.0);
	float NdotL = max(dot(N, L), 0.0);
	float ggx2 = GeometrySchlickGGX(NdotV, roughness);
	float ggx1 = GeometrySchlickGGX(NdotL, roughness);

	return ggx1 * ggx2;
}
// ----------------------------------------------------------------------------
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
	return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
}

// ----------------------------------------------------------------------------
void main()
{
	vec3 baseColor = material.baseColor;
	if (isTexImage) {
		FragColor = texture(textureImg, ioTexcoord);
		if (FragColor.a < 0.1) {
			FragColor.a = 0.0;
		} else if (!isZhiche) {
			if (baseColor.r != 0.0 || baseColor.g != 0.0 || baseColor.b != 0.0)// valid color -- Not 'Black'
				FragColor = mix(FragColor, vec4(baseColor, 1.0), 0.6);
			FragColor = vec4(fadeOutView3dEdge(FragColor.rgb), FragColor.a);
		}
		return;
	}

	vec3 color = baseColor;
	float GlobalAmbient_Compnt = pbrGlobalAmbientCompnt;
	if (isZhiche)// keep zhiche's ambient independent
		GlobalAmbient_Compnt = 4.0;
	/**************** appling PBR material with Light **************/
	if (enableLight) {
		vec3 N = normalize(ioNormal);
		vec3 V = normalize(ioViewVector);

		// calculate reflectance at normal incidence; if dia-electric (like plastic) use F0 
		// of 0.04 and if it's a metal, use the material.baseColor color as F0 (material.metallic workflow)    
		vec3 F0 = vec3(F0_Compnt);
		F0 = mix(F0, baseColor, material.metallic);

		// calculate per-light radiance
		vec3 L = normalize(-directional.worldDirection);// Vertex point to Light
		vec3 H = normalize(V + L);
		vec3 radiance = directional.color * directional.intensity;

		// Cook-Torrance BRDF
		float NDF = DistributionGGX(N, H, material.roughness);   
		float G   = GeometrySmith(N, V, L, material.roughness);      
		vec3 F    = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0);
		   
		vec3 numerator    = NDF * G * F; 
		float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001; // + 0.0001 to prevent divide by zero
		vec3 specular = numerator / denominator;
		
		// kS is equal to Fresnel
		vec3 kS = F;
		// for energy conservation, the diffuse and specular light can't
		// be above 1.0 (unless the surface emits light); to preserve this
		// relationship the diffuse component (kD) should equal 1.0 - kS.
		vec3 kD = vec3(1.0) - kS;
		// multiply kD by the inverse metalness such that only non-metals 
		// have diffuse lighting, or a linear blend if partly metal (pure metals
		// have no diffuse light).
		kD *= 1.0 - material.metallic;

		// scale light by NdotL
		float NdotL = max(dot(N, L), 0.0);

		// reflectance equation
		vec3 Lo = (kD * baseColor / PI + specular) * radiance * NdotL;  // note that we already multiplied the BRDF by the Fresnel (kS) so we won't multiply by kS again
	
		// ambient lighting (note that the next IBL tutorial will replace 
		// this ambient lighting with environment lighting).
		vec3 ambient = vec3(GlobalAmbient_Compnt) * baseColor * AO_Factor;

		color = ambient + Lo;

		// HDR tonemapping
		color = color / (color + vec3(1.0));
		// gamma correct
		color = pow(color, vec3(1.0/2.2));
	}

	if (isZhiche || !enableLight) {
		FragColor = vec4(color, 1.0);
		return;
	}

	if (ellipse.isActive)
		color = attachEllipseEffect(color);

	FragColor = vec4(fadeOutView3dEdge(color), 1.0);
}